﻿using System;
using System.Collections.Generic;

using OCXS;
using OCXS.OCXSServiceLibrary.OPCXMLDA10;

namespace OCXS {

	namespace OCXSServiceLibrary {
        
		public partial class OCXSService {
            
			/// <summary>
            /// <para>
            /// Implements OPC XML DA 1.0 GetProperties service. 
            /// </para>
            /// <para>
            /// See OPC XML DA 1.0 specification for more information about GetProperties service.
            /// </para>
			/// </summary>
            /// <remarks>
            /// <para>
            ///     <b>Following there's a list of implemented (or not) standard features.</b>
            /// </para>
            /// <para>
            ///     The following <c>GetPropertiesRequest</c> standard features are (or not) implemented.
            /// 
            ///     <ul>
            ///         <li><b>ClientRequestHandle: </b> supported.</li>
            ///         <li><b>ItemIDs: </b> supported.</li>
            ///         <li><b>ItemPath: </b> ignored.</li>
            ///         <li><b>LocaleID: </b> supported.</li>
            ///         <li><b>PropertyNames: </b> not supported.</li>
            ///         <li><b>ReturnAllProperties: </b> supported (assumed always <c>true</c>).</li>
            ///         <li><b>ReturnErrorText: </b> not supported.</li>
            ///         <li><b>ReturnPropertyValues: </b> supported.</li>
            ///     </ul>
            /// </para>
            /// 
            /// <para>
            ///     The following <c>GetPropertiesResponse</c> standard features are (or not) implemented.
            ///     <ul>
            ///         
            ///         <li>
            ///             <b>Errors: </b> One error per time is reported. The following errors are supported:
            ///             <ul>
            ///                 <li><c>E_OUTOFMEMORY</c></li>
            ///                 <li><c>E_FAIL</c></li>
            ///             </ul>
            ///         </li>
            ///
            ///         <li>
            ///             <b>GetPropertiesResult: </b> the following features are (or not) supported:
            ///             <ul>
            ///                 <li><b>ClientRequestHandle: </b> supported.</li>
            ///                 <li><b>RcvTime: </b> supported (evaluated as first instruction).</li>
            ///                 <li><b>ReplyTime: </b> supported (evaluated as last instruction).</li>
            ///                 <li><b>RevisedLocaleID: </b> supported (<c>en-us</c>).</li>
            ///                 <li><b>ServerState: </b> supported (<c>running</c>).</li>
            ///             </ul>
            ///         </li>
            /// 
            ///         <li>
            ///             <b>PropertyList: </b> the following features are (or not) supported:
            ///             <ul>
            ///                 <li><b>ItemName: </b> supported.</li>
            ///                 <li><b>ItemPath: </b> supported.</li>
            ///                 <li>
            ///                     <b>Properties: </b> 
            ///                     supported, but no standard ones. All available ItemProperties are always returned. Supported ItemProperties are a set of custom properties. 
            ///                     <ul>
            ///                         <li><b>Description: </b> supported.</li>
            ///                         <li><b>ItemName: </b> supported.</li>
            ///                         <li><b>ItemPath: </b> supported.</li>
            ///                         <li><b>Name: </b> supported.</li>
            ///                         <li><b>ResultID: </b> not supported.</li>
            ///                         <li><b>Value: </b> supported.</li>
            ///                     </ul>
            ///                 </li>
            ///                 <li><b>ResultID: </b> supported. Only <c>E_UNKNOWNITEMNAME</c> error is supported.</li>
            ///             </ul>
            ///         </li>
            /// 
            ///     </ul>
            /// </para>
            /// 
            /// <code>
            /// // The following code can be found in OCXSClient and 
            /// // shows how to call GetProperties service.
            /// class Program {
            ///
            ///     private const string EndpointAddress = "http://localhost/OCXS/OCXS.svc";
            ///     
            ///     static void Main(string[] args) {
            ///     
            ///         // The following gets properties of default defined
            ///         // Items in GetProperties(), specifying the return
            ///         // of property values in the response.
            ///         GetProperties("en-us", "0x00FF", true);
            ///     }
            ///     
            ///     public static GetPropertiesResponse GetProperties(
            ///         string localeID,
            ///         string clientRequestHandle,
            ///         bool returnPropertyValues,
            ///         bool returnAllProperties = true,
            ///         ItemIdentifier[] itemIDs = null,
            ///         // Not supported features formal parameters down here
            ///         string itemPath = "",
            ///         System.Xml.XmlQualifiedName[] propertyNames = null,
            ///         bool returnErrorText = false) 
            ///     {
            ///         // Get the channel.
            ///         // GetOCXSChannel can be found in OCXSClient.
            ///         IOCXSService svc = GetOCXSChannel(EndpointAddress);
            /// 
            ///         // Let's create a valid ItemIdentifier[] if not provided.
            ///         // This way is shown how to create a valid ItemIdentifier array.
            ///         ItemIdentifier[] ids = itemIDs;
            ///         if (ids == null) {
            ///                 
            ///             // There are two 'main ways' to obtain an ItemIdentifier[]:
            ///             //   1. Creating a static fixed size array.
            ///             //
            ///             //   2. Create a List&lt;ItemIdentifier&gt; and call ToArray() method
            ///             //      when finished adding ItemIdentifiers.
            ///             //
            ///             // The solution (2) follows.
            /// 
            ///             // Imagine having in input some pair containing (ItemPath, ItemName).
            ///             // NOTE: This is just an example about "having something in input", not
            ///             //       meant to be the best solution to specify the Items you want.
            ///             //       Acting this way, ItemPath corresponds to the Key, while ItemName 
            ///             //       corresponds to Value.
            ///             KeyValuePair&lt;string, string&gt;[] input = 
            ///                 new KeyValuePair&lt;string, string&gt;[2] { 
            ///                     new KeyValuePair&lt;string, string&gt;("Root/Fold1/", "MyVar1"),
            ///                     new KeyValuePair&lt;string, string&gt;("Root/", "MyVar45")
            ///                 }; 
            /// 
            ///             List&lt;ItemIdentifier&gt; l = new List&lt;ItemIdentifier&gt;();
            /// 
            ///             // For each element in input create an ItemIdentifier.
            ///             foreach (KeyValuePair&lt;string, string&gt; el in input) {
            ///                 ItemIdentifier id = new ItemIdentifier();
            ///                 id.ItemPath = el.Key;
            ///                 id.ItemName = el.Value;
            /// 
            ///                 l.Add(id);
            ///             }
            /// 
            ///             // Once added every input element, convert the list to array and
            ///             // the trick is done.
            ///             ids = l.ToArray();
            ///         }
            /// 
            ///         // Create the request.
            ///         GetPropertiesRequest GPR =
            ///             new GetPropertiesRequest(
            ///                 ids,
            ///                 propertyNames,
            ///                 localeID,
            ///                 clientRequestHandle,
            ///                 itemPath,
            ///                 returnAllProperties,
            ///                 returnPropertyValues,
            ///                 returnErrorText);
            /// 
            ///         // Perform the call to service and get the response.
            ///         GetPropertiesResponse res = svc.GetProperties(GPR);
            /// 
            ///         // Print the response.
            ///         // PrintResponse can be found in OCXSClient.
            ///         PrintResponse(res);
            /// 
            ///         return res;
            ///     } 
            /// </code>
            /// 
            /// </remarks>
            /// <param name="request">The <c>GetPropertiesRequest</c> issued by client according to OCP XML DA 1.0 specification.</param>
            /// <returns>A <c>GetPropertiesResponse</c> according to OPC XML DA 1.0 specification.</returns>
			public GetPropertiesResponse GetProperties(GetPropertiesRequest request) {
                /**
                 *  For future implementations:
                 *      Other error handling must be performed on this service method according
                 *      to OPC XML DA 1.0 standard.
                 *      Remember to add new exception handling catch blocks before generic Exception 
                 *      catch block.
                 */

                GetPropertiesResponse GPR = null;
                // Get the reply base
                ReplyBase RB = this.GetReplyBase(request.ClientRequestHandle, request.LocaleID);

                /**
                 *  For future implemetations:
                 *      request.ItemIDs contains the identifier of items whose
                 *      properties have been requested by the client.
                 *      
                 *      request.PropertyNames contains the names of properties in which  
                 *      client is interested into.
                 *      
                 *      So calling GetPropertyReplyLists(), if request.ReturnAllProperties
                 *      is not true, should return just some properties.
                 */

                try {

                    // Create the response
                    GPR = new GetPropertiesResponse(
                            RB,
                            this.GetPropertyReplyLists(
                                request.ItemIDs,
                                request.ReturnAllProperties,
                                request.ReturnPropertyValues),
                            null);

                    RB.ReplyTime = DateTime.Now;

                    return GPR;

                } catch (OutOfMemoryException e) {

                    RB.ReplyTime = DateTime.Now;
                    
                    // Return proper out of memory error message.
                    return new GetPropertiesResponse(
                            RB,
                            null,
                            new OPCError[] { this.BuildOPCError("E_OUTOFMEMORY", e.Message) });

                } catch (Exception e) {

                    RB.ReplyTime = DateTime.Now;

                    // Return proper out of memory error message.
                    return new GetPropertiesResponse(
                            RB,
                            null,
                            new OPCError[] { this.BuildOPCError("E_FAIL", e.Message) });
                }

			}

            /// <summary>
            /// Returns an array of <c>PropertyReplyList</c> as requested.
            /// </summary>
            /// <remarks>
            /// <b>Please note that <c>returnall</c> param is ignored.</b>
            /// </remarks>
            /// <param name="itemids">Array of <c>ItemIdentifier</c> which provides requested Item's properties.</param>
            /// <param name="returnall">Specifies whether or not return all properties for requested Items. </param>
            /// <param name="returnvalues">Specifies whether or not return values for requested Items.</param>
            /// <returns>An array of <c>PropertyReplyList</c> as requested.</returns>
            private PropertyReplyList[] GetPropertyReplyLists(ItemIdentifier[] itemids, bool returnall = true, bool returnvalues = true) {

                List<PropertyReplyList> prll = new List<PropertyReplyList>();
                
                foreach (ItemIdentifier ii in itemids) {

                    PropertyReplyList prl = new PropertyReplyList();
                    prl.ItemName = ii.ItemName;
                    prl.ItemPath = ii.ItemPath;

                    ItemProperty[] ii_props = this.GetItemPropertiesByItemId(ii.ItemPath, ii.ItemName, returnvalues);

                    if (ii_props == null) {
                        // Error management.
                        prl.ResultID = new System.Xml.XmlQualifiedName("E_UNKNOWNITEMNAME", "http://opcfoundation.org/webservices/XMLDA/1.0/");
                        prl.Properties = null;

                    } else {
                        /**
                         * For future implemetations:
                         *     Replace the following code with:
                         *     
                         *     if (returnall)
                         *         // ... put all properties in prl.Properties
                         *     else
                         *         // ... put just requested properties in prl.Properties
                         *     
                         */
                        prl.Properties = ii_props;
                        
                    }

                    prll.Add(prl);

                }

                return prll.ToArray();

            } /* GetPropertyReplyLists */
            
		}
        
	}

}
